package top.eggcode.component.query.predicate;

import top.eggcode.common.exception.ServiceLogicException;
import top.eggcode.component.query.model.Expression;
import top.eggcode.component.query.model.ExpressionImpl;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;

/**
 * Title: 复合谓语
 * Description: TODO
 * Date: 2021/9/14 22:53
 *
 * @author JiaQi Ding
 * @version 1.0
 */
public class CompoundPredicate extends AbstractPredicateImpl {

    private final LinkOperator operator;

    private final List<Expression<Boolean>> expressions;

    private final List<Expression<?>> parameters;

    /**
     * 括号
     */
    private final boolean isBind;

    public CompoundPredicate(LinkOperator operator) {
        this.isBind = false;
        this.operator = operator;
        this.expressions = new LinkedList<>();
        this.parameters = new LinkedList<>();
    }

    public CompoundPredicate(LinkOperator operator, boolean isBind) {
        this.isBind = isBind;
        this.operator = operator;
        this.expressions = new LinkedList<>();
        this.parameters = new LinkedList<>();
    }

    public CompoundPredicate(LinkOperator operator, SimplePredicate... expressions) {
        this(operator);
        this.expressions.addAll(Arrays.asList(expressions));
    }

    @Override
    public LinkOperator getOperator() {
        return operator;
    }

    @Override
    public List<Expression<Boolean>> getExpressions() {
        return expressions;
    }

    @Override
    public boolean isJunction() {
        return true;
    }

    public List<Expression<?>> getParameters() {
        return parameters;
    }

    public CompoundPredicate and(String predicate) {
        this.expressions.add(new OriginalPredicate(predicate.trim()));
        return this;
    }

    public CompoundPredicate and(String expression, String parameter) {
        this.expressions.add(new OriginalPredicate(expression.trim()));
        this.parameters.add(new ExpressionImpl<>(parameter, String.class));
        return this;
    }

    public CompoundPredicate and(String expression, int parameter) {
        this.expressions.add(new OriginalPredicate(expression.trim()));
        this.parameters.add(new ExpressionImpl<>(parameter, Integer.class));
        return this;
    }

    public CompoundPredicate and(String expression, long parameter) {
        this.expressions.add(new OriginalPredicate(expression.trim()));
        this.parameters.add(new ExpressionImpl<>(parameter, Long.class));
        return this;
    }

    public CompoundPredicate and(Consumer<CompoundPredicate> condition) {
        CompoundPredicate predicate = new CompoundPredicate(LinkOperator.AND, true);
        condition.accept(predicate);
        this.expressions.add(predicate);

        return this;
    }

    public CompoundPredicate and() {
        CompoundPredicate predicate = new CompoundPredicate(LinkOperator.AND, true);
        this.expressions.add(predicate);

        return predicate;
    }

    public CompoundPredicate or() {
        CompoundPredicate predicate = new CompoundPredicate(LinkOperator.OR);
        this.expressions.add(predicate);
        return predicate;
    }

    public CompoundPredicate or(Consumer<CompoundPredicate> condition) {
        if (this.expressions.size() == 0) {
            throw new ServiceLogicException("OR 关键字左边必须存在一个表达式");
        }
        CompoundPredicate predicate = new CompoundPredicate(LinkOperator.OR, true);
        condition.accept(predicate);
        this.expressions.add(predicate);

        return this;
    }


    public CompoundPredicate or(String condition) {
        this.expressions.add(
                new CompoundPredicate(LinkOperator.OR,
                        new OriginalPredicate(condition.trim())
                )
        );
        return this;
    }

    public CompoundPredicate not(Consumer<CompoundPredicate> condition) {

        CompoundPredicate predicate = new CompoundPredicate(LinkOperator.OR, true);

        // 创建否定谓语，将复合谓语包装，NOT + 谓语

        condition.accept(predicate);
        this.expressions.add(predicate);

        return this;
    }

    /**
     * Not equal !=
     */
    public CompoundPredicate nEq(String column, String value) {
        String predicateComparison = column + " <> ?";
        this.parameters.add(new ExpressionImpl<>(value, String.class));
        return this.and(predicateComparison);
    }

    public CompoundPredicate nEq(String column, int value) {
        String predicateComparison = column + " <> " + value;
        return this.and(predicateComparison);
    }

    public CompoundPredicate eq(String column, String value) {
        String predicateComparison = column + " = ?";
        return this.and(predicateComparison, value);
    }

    public CompoundPredicate eq(String column, int value) {
        String predicateComparison = column + " = " + value;
        return this.and(predicateComparison);
    }

    public CompoundPredicate eq(String column, boolean value) {
        String predicateComparison = null;
        if (value) {
            predicateComparison = column + " = true";
        } else {
            predicateComparison = column + " = false";
        }
        return this.and(predicateComparison);
    }

    public CompoundPredicate gt(String column, int value) {
        String predicateComparison = column + " > " + value;
        return this.and(predicateComparison);
    }

    public CompoundPredicate gtOrEq(String column, int value) {
        String predicateComparison = column + " >= " + value;
        return this.and(predicateComparison);
    }

    public CompoundPredicate lt(String column, int value) {
        String predicateComparison = column + " < " + value;
        return this.and(predicateComparison);
    }

    public CompoundPredicate ltOrEq(String column, int value) {
        String predicateComparison = column + " <= " + value;
        return this.and(predicateComparison);
    }

    public CompoundPredicate like(String column, String pattern) {
        this.parameters.add(new ExpressionImpl<>(pattern, String.class));
        return this.and(column + " NOT LIKE ?");
    }

    public CompoundPredicate notLike(String column, String pattern) {
        this.parameters.add(new ExpressionImpl<>(pattern, String.class));
        return this.and(column + " NOT LIKE ?");
    }

    public CompoundPredicate likeAny(String column, String pattern) {
        return this.and(column + " LIKE ?", "%" + pattern + "%");
    }

    public CompoundPredicate likeRight(String column, String pattern) {
        return this.and(column + " LIKE ?", pattern + "%");
    }

    public CompoundPredicate likeLeft(String column, String pattern) {
        return this.and(column + " LIKE ?", "%" + pattern);
    }

    public CompoundPredicate isNull(String column) {
        return this.and(column + " IS NULL");
    }

    public CompoundPredicate isNotNull(String column) {
        return this.and(column + " IS NOT NULL");
    }

    public <T> CompoundPredicate in(String expression, List<T> values) {
        if (values.size() == 0) {
            return this;
        }
        InPredicate<T> in = new InPredicate<>(new ExpressionImpl<>(expression, String.class), values);
        return this.and(in.render());

    }

    public <T> CompoundPredicate notIn(String expression, List<T> values) {
        if (values.size() == 0) {
            return this;
        }
        InPredicate<T> in = new InPredicate<>(new ExpressionImpl<>(expression, String.class), values);
        NegatedPredicateWrapper notIn = (NegatedPredicateWrapper) in.not();
        return this.and(notIn.render());

    }

    @Override
    public String render(boolean isNegated) {
        return render();
    }

    @Override
    public String render() {
        StringBuilder sentence = new StringBuilder();
        if (this.operator == LinkOperator.OR) {
            if (expressions.size() == 1) {
                sentence.append("OR ").append(((SimplePredicate) this.expressions.get(0)).render());
                // 如果谓语数量大于1，需要添加圆括号
            } else if (expressions.size() > 1 && isBind) {
                sentence.append("OR (");
                sentence.append(((SimplePredicate) expressions.get(0)).render());
                for (int i = 1; i < expressions.size(); i++) {
                    SimplePredicate expression = (SimplePredicate) expressions.get(i);
                    sentence.append(" ");
                    if (expression.getClass() == CompoundPredicate.class) {
                        sentence.append(expression.render());
                    } else {
                        sentence.append("AND ").append(expression.render());
                    }
                }
                sentence.append(")");
            } else if (expressions.size() > 1) {
                sentence.append("OR ");
                sentence.append(((SimplePredicate) expressions.get(0)).render());
                for (int i = 1; i < expressions.size(); i++) {
                    SimplePredicate expression = (SimplePredicate) expressions.get(i);
                    sentence.append(" ");
                    if (expression.getClass() == CompoundPredicate.class) {
                        sentence.append(expression.render());
                    } else {
                        sentence.append("AND ").append(expression.render());
                    }
                }
            }
        } else {
            if (expressions.size() >= 1) {
                if (isBind) {
                    sentence.append("AND (");
                    sentence.append(((SimplePredicate) expressions.get(0)).render());
                    for (int i = 1; i < expressions.size(); i++) {
                        SimplePredicate expression = (SimplePredicate) expressions.get(i);
                        sentence.append(" ");
                        // 如果是复合谓语，无需添加and
                        if (expression.getClass() == CompoundPredicate.class) {
                            sentence.append(expression.render());
                        } else {
                            sentence.append("AND ").append(expression.render());
                        }
                    }
                    sentence.append(")");
                } else {
                    sentence.append(((SimplePredicate) expressions.get(0)).render());
                    for (int i = 1; i < expressions.size(); i++) {
                        SimplePredicate expression = (SimplePredicate) expressions.get(i);
                        sentence.append(" ");
                        // 如果是复合谓语，无需添加and
                        if (expression.getClass() == CompoundPredicate.class) {
                            sentence.append(expression.render());
                        } else {
                            sentence.append("AND ").append(expression.render());
                        }
                    }
                }

            }
        }
        return sentence.toString();
    }


}
